﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Linq;
using System.Diagnostics;
using Young3.FMSearch.Core.Entities;
using Young3.FMSearch.Core.Attributes;
using Young3.FMSearch.Core.Entities.GameVersions;

namespace Young3.FMSearch.Core.Managers
{
    internal class GameManager
    {
        private bool fmLoaded;
        private bool fmLoading;

        public GameManager()
        {
            fmLoaded = false;
            fmLoading = false;
        }

        public bool FMLoaded
        {
            get { return fmLoaded; }
        }

        public bool FMLoading
        {
            get { return fmLoading; }
            set { fmLoading = value; }
        }

        public IVersion Version
        {
            get;
            private set;
        }

        /// <summary>
        /// Finds and loads the game in memory
        /// </summary>
        /// <returns>A bool indicating whether the game is running</returns>
        public bool CheckProcessAndGame()
        {
            FMProcess fmProcess = new FMProcess();
            Process[] fmProcesses = Process.GetProcessesByName("fm");
            Process[] wsmProcesses = Process.GetProcessesByName("wsm");
            
            if (fmProcesses.Length > 0 || wsmProcesses.Length > 0)
            {
                Process activeProcess;
                if (fmProcesses.Length > 0)
                    activeProcess = fmProcesses[0];
                else
                    activeProcess = wsmProcesses[0];

                fmProcess.Pointer = ProcessMemoryApi.OpenProcess(0x38, 1, (uint)activeProcess.Id);
                fmProcess.EndPoint = ProcessManager.GetProcessEndPoint(fmProcess.Pointer);
                fmProcess.Process = activeProcess;

                ProcessManager.fmProcess = fmProcess;

                fmProcess.VersionDescription = fmProcess.Process.MainModule.FileVersionInfo.ProductVersion;

                // search for the curr version
                foreach(var versionType in Assembly.GetCallingAssembly().GetTypes().Where(t=>typeof(IIVersion).IsAssignableFrom(t)))
                {
                    if (versionType.IsInterface) continue;

                    var instance = (IIVersion)Activator.CreateInstance(versionType);

                    if(instance.SupportsProcess(fmProcess))
                    {
                        Version = instance;
                        break;
                    }
                }

                fmLoaded = (Version != null);
            }
            return fmLoaded;
        }

        public void findOffsets()
        {
            Stopwatch sw = new Stopwatch(); sw.Start();

            FMProcess fmProcess = new FMProcess();
            Process[] fmProcesses = Process.GetProcessesByName("fm");
            Process[] wsmProcesses = Process.GetProcessesByName("wsm");

            if (fmProcesses.Length > 0 || wsmProcesses.Length > 0)
            {
                Process activeProcess;
                if (fmProcesses.Length > 0)
                    activeProcess = fmProcesses[0];
                else
                    activeProcess = wsmProcesses[0];

                fmProcess.Pointer = ProcessMemoryApi.OpenProcess(0x38, 1, (uint)activeProcess.Id);
                fmProcess.EndPoint = ProcessManager.GetProcessEndPoint(fmProcess.Pointer);
                fmProcess.Process = activeProcess;

                ProcessManager.fmProcess = fmProcess;

                fmProcess.VersionDescription = fmProcess.Process.MainModule.FileVersionInfo.ProductVersion;

                // search for the curr version
                foreach (var versionType in Assembly.GetCallingAssembly().GetTypes().Where(t => typeof(IIVersion).IsAssignableFrom(t)))
                {
                    if (versionType.IsInterface) continue;

                    var instance = (IIVersion)Activator.CreateInstance(versionType);

                    if (instance.SupportsProcess(fmProcess))
                    {
                        Version = instance;
                        break;
                    }
                }

                fmLoaded = (Version != null);
            }

            Console.WriteLine(string.Format("Found game in memory ({0} ms)", sw.ElapsedMilliseconds));
            sw.Reset(); sw.Start();
            List<int> memoryAddresses;
            int numberOfObjects = -1;
            int continentCounter = 0;
            int baseObjectPointer = 0;

            while (numberOfObjects != 7)
            {
                //we use baseObjectPointer in this case
                baseObjectPointer = 0x256e3d0;
                baseObjectPointer += continentCounter;

                int memoryAddress = baseObjectPointer;

                memoryAddress = ProcessManager.ReadInt32(memoryAddress);

                memoryAddress = ProcessManager.ReadInt32(memoryAddress + 0x28);

                numberOfObjects = ProcessManager.ReadInt32(memoryAddress + 4);

                if (numberOfObjects == 7)
                {
                    //we have a pointer here, so we do one last lookup
                    memoryAddress = ProcessManager.ReadInt32(memoryAddress);

                    memoryAddresses = ObjectManager.GetMemoryAddresses(memoryAddress, numberOfObjects);

                    if (memoryAddresses.Count != 14444)
                    {
                        numberOfObjects = 0;
                    }
                }
                continentCounter++;

                if (continentCounter > 3000000)
                {
                    Console.WriteLine("Not found");
                    return;
                }
            }
            continentCounter--;
            Console.WriteLine("Continent offset found: 0x" + continentCounter.ToString("X") + " (" + sw.ElapsedMilliseconds + " ms)");
            sw.Reset(); sw.Start();

            Console.ReadKey(true);
        }
    }
}
